#ifndef NAV_MESH_H
#define NAV_MESH_H

#include <stdio.h>
#include <string.h>
#include <list>
#include <fstream>
#include <iostream>
#include <vector>

#include "DetourCommon.h"
#include "DetourNavMesh.h"
#include "DetourNavMeshBuilder.h"
#include "DetourNavMeshQuery.h"
#include "Recast.h"

struct NavMeshSetHeader {
  int magic;
  int version;
  int numTiles;
  dtNavMeshParams params;
};

struct NavMeshTileHeader {
  dtTileRef tileRef;
  int dataSize;
};

enum SamplePolyFlags {
  SAMPLE_POLYFLAGS_WALK = 0x01,      // Ability to walk (ground, grass, road)
  SAMPLE_POLYFLAGS_SWIM = 0x02,      // Ability to swim (water).
  SAMPLE_POLYFLAGS_DOOR = 0x04,      // Ability to move through doors.
  SAMPLE_POLYFLAGS_JUMP = 0x08,      // Ability to jump.
  SAMPLE_POLYFLAGS_DISABLED = 0x10,  // Disabled polygon
  SAMPLE_POLYFLAGS_ALL = 0xffff      // All abilities.
};

enum TestPolyAreas {
  TEST_POLYAREA_GROUND,
  TEST_POLYAREA_WATER,
  TEST_POLYAREA_ROAD,
  TEST_POLYAREA_DOOR,
  TEST_POLYAREA_GRASS,
  TEST_POLYAREA_JUMP,
};

enum TestPolyFlags {
  TEST_POLYFLAGS_WALK = 0x01,      // Ability to walk (ground, grass, road)
  TEST_POLYFLAGS_SWIM = 0x02,      // Ability to swim (water).
  TEST_POLYFLAGS_DOOR = 0x04,      // Ability to move through doors.
  TEST_POLYFLAGS_JUMP = 0x08,      // Ability to jump.
  TEST_POLYFLAGS_DISABLED = 0x10,  // Disabled polygon
  TEST_POLYFLAGS_ALL = 0xffff      // All abilities.
};

enum ToolMode {
  TOOLMODE_PATHFIND_FOLLOW,
  TOOLMODE_PATHFIND_STRAIGHT,
  TOOLMODE_PATHFIND_SLICED,
  TOOLMODE_RAYCAST,
  TOOLMODE_DISTANCE_TO_WALL,
  TOOLMODE_FIND_POLYS_IN_CIRCLE,
  TOOLMODE_FIND_POLYS_IN_SHAPE,
  TOOLMODE_FIND_LOCAL_NEIGHBOURHOOD
};

// Returns a random number [0..1]
static float frand() {
  //	return ((float)(rand() & 0xffff)/(float)0xffff);
  return (float)rand() / (float)RAND_MAX;
}

static const int TILECACHESET_MAGIC =
    'T' << 24 | 'S' << 16 | 'E' << 8 | 'T';  //'TSET';
static const int TILECACHESET_VERSION = 1;

static const int NAVMESHSET_MAGIC =
    'M' << 24 | 'S' << 16 | 'E' << 8 | 'T';  //'MSET';
static const int NAVMESHSET_VERSION = 1;

static const int MAX_POLYS = 8192;
static const int MAX_SMOOTH = 8192;

class NavMesh {
 public:
  int m_straightPathOptions;
  dtStatus m_pathFindStatus;
  int m_pathIterNum;
  ToolMode m_toolMode;
  dtNavMesh *m_navMesh;
  dtNavMeshQuery *m_navQuery;
  dtQueryFilter m_filter;
  float m_polyPickExt[3];

  float m_spos[3];
  float m_epos[3];
  dtPolyRef m_startRef;
  dtPolyRef m_endRef;
  dtPolyRef m_polys[MAX_POLYS];
  dtPolyRef m_parent[MAX_POLYS];
  int m_npolys;
  int m_nsmoothPath;
  float m_smoothPath[MAX_SMOOTH * 3];

  float m_straightPath[MAX_POLYS * 3];
  unsigned char m_straightPathFlags[MAX_POLYS];
  dtPolyRef m_straightPathPolys[MAX_POLYS];
  int m_nstraightPath;
  float m_hitNormal[3];
  std::list<int32_t> m_pathIndex;

  NavMesh();
  ~NavMesh();

  dtNavMesh *LoadSoloMeshFile(const char *file_name);
  bool LoadSoloNavMesh(const char *file_name);

  void RandomPoints(int count);
  void FindPath(float sp[3], float ep[3]);
};
#endif